// Copyright 2013 Petar Maymounkov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package chain

import (
	"net"
	"time"

	"circuit/kit/tele/trace"
)

type Transport struct {
	frame   trace.Frame
	stat    Stat
	carrier Carrier
}

func NewTransport(frame trace.Frame, carrier Carrier) *Transport {
	t := &Transport{
		frame:   frame,
		carrier: carrier,
	}
	t.frame.Bind(t)
	t.stat.Init()
	/*
		go func() {
			s := t.Subscribe()
			for {
				u := s.Wait()
				t.frame.Printf("Status: NDial=%d NAccept=%d", u.NDial, u.NAccept)
				//println(t.stat.Dump())
			}
		}()
	*/
	return t
}

func (t *Transport) Subscribe() *Subscriber {
	return t.stat.Subscribe()
}

const CarrierRedialTimeout = time.Second

// Dial creates a new chain connection to addr.
// Dial returns instanteneously (it does not wait on I/O operations) and always succeeds, returning a non-nil connection object.
func (t *Transport) Dial(addr net.Addr) *Conn {
	id := chooseChainID()
	t.stat.addDC(id, addr)
	dc := newDialConn(t.frame.Refine(addr.String()), id, addr,
		func() (net.Conn, error) {
			return t.carrier.Dial(addr)
		},
		func() {
			t.stat.scrubDC(id)
		},
	)
	return &dc.Conn
}

func (t *Transport) Listen(addr net.Addr) *Listener {
	return NewListener(t.frame.Refine("listener"), &t.stat, t.carrier, addr)
}
